package com.hencoder.hencoderpracticedraw1.practice;

import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.graphics.RectF;
import android.graphics.Shader;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.DecelerateInterpolator;

import com.hencoder.hencoderpracticedraw1.MiscUtil;

import java.util.List;

/**
 * Created by zhanghs on 2017/7/14.
 */

public class Practice13BezierWaterWaveView extends View{
    private final String Tag = Practice13BezierWaterWaveView.class.getSimpleName();
    private Context mContext;
    private int mMaxValue = 100;
    private int View_Default_Size = 300;
    private int mDefaultSize;
    /*完整的波浪长度*/
    private int mViewWidth;
    /*完整的波浪高度*/
    private int mViewHeight;
    /*水位线*/
    private int mLevelHeight;
    /*波长*/
    private int mWaveWidth= 400;
    /*波高*/
    private int mWaveHeight = 200;
    /**
     * 被隐藏的最左边的波形
     */
    private float mLeftSide;
    /**
     * 移动的距离
     */
    private float mMoveLen;
    /**
     * 水波平移速度
     */
    public static final float SPEED = 5f;
    /**
     * 二阶贝塞尔曲线
     */
    private List<Point> mPointsList;

    private Paint mWavePaint;
    private Paint linePaint;
    /**
     * 进度文字画笔
     */
    private Paint mTextPaint;

    /**
     * 边界画笔
     */
    private Paint mPaintCir;

    /**
     * 波纹路径
     */
    private Path mWavePath;
    private Path mWaveStrokePath;
    /*圆弧宽度*/
    private float mArcWidth = 20;
    private Point mCenterPoint;
    private RectF mRectF;
    private float mRadius;
    private float mPrecent;
    private float mCurrVaule;
    private Paint mBgPaintCir;
    /*WaveLoadingView*/
    private Matrix mShaderMatrix;
    private Paint mWaveBgPaint;
    private float mWaterLevelRatio = 1f;
    private float mAmplitudeRatio;
    private int mCanvasSize;
    private BitmapShader mWaveShader;
    /**
     * +------------------------+
     * | wave length - 波长      |__________
     * |   /\          |   /\   |  |
     * |  /  \         |  /  \  | amplitude - 振幅
     * | /    \        | /    \ |  |
     * |/      \       |/      \|__|_______
     * |        \      /        |  |
     * |         \    /         |  |
     * |          \  /          |  |
     * |           \/           | water level - 水位
     * |                        |  |
     * |                        |  |
     * +------------------------+__|_______
     */
    private static final float DEFAULT_AMPLITUDE_RATIO = 0.1f;
    private static final float DEFAULT_AMPLITUDE_VALUE = 50.0f;
    private static final float DEFAULT_WATER_LEVEL_RATIO = 0.5f;
    private static final float DEFAULT_WAVE_LENGTH_RATIO = 1.0f;
    private static final float DEFAULT_WAVE_SHIFT_RATIO = 0.0f;
    private static final int DEFAULT_WAVE_PROGRESS_VALUE = 50;
    private static final int DEFAULT_WAVE_COLOR = Color.parseColor("#212121");
    private static final int DEFAULT_WAVE_BACKGROUND_COLOR = Color.parseColor("#00000000");
    private static final int DEFAULT_TITLE_COLOR = Color.parseColor("#212121");
    private static final int DEFAULT_STROKE_COLOR = Color.TRANSPARENT;
    private static final float DEFAULT_BORDER_WIDTH = 0;
    private static final float DEFAULT_TITLE_STROKE_WIDTH = 0;
    // This is incorrect/not recommended by Joshua Bloch in his book Effective Java (2nd ed).
    private static final int DEFAULT_WAVE_SHAPE = WaveLoadingView.ShapeType.CIRCLE.ordinal();
    private static final int DEFAULT_TRIANGLE_DIRECTION = WaveLoadingView.TriangleDirection.NORTH.ordinal();
    private static final int DEFAULT_ROUND_RECTANGLE_X_AND_Y = 30;
    private static final float DEFAULT_TITLE_TOP_SIZE = 18.0f;
    private static final float DEFAULT_TITLE_CENTER_SIZE = 22.0f;
    private static final float DEFAULT_TITLE_BOTTOM_SIZE = 18.0f;
    private float mDefaultWaterLevel;
    private float mWaveShiftRatio = DEFAULT_WAVE_SHIFT_RATIO;
    private Paint mBorderPaint;
    private int mShapeType;
    private Bitmap bitmapBuffer;
    private int mWaveColor;
    private int mWaveBgColor;

    public Practice13BezierWaterWaveView(Context context) {
        this(context,null);
    }

    public Practice13BezierWaterWaveView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public Practice13BezierWaterWaveView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mContext = context;
        mCenterPoint = new Point();
        mRectF = new RectF();
        mDefaultSize = MiscUtil.dipToPx(context, View_Default_Size);
        init();
        setProgress(35);
    }

    private void init() {
        linePaint = new Paint();
        linePaint.setAntiAlias(true);
        linePaint.setColor(Color.RED);
        linePaint.setStrokeWidth(5);

        mWavePaint = new Paint();
        mWavePaint.setAntiAlias(true);
        mWavePaint.setColor(Color.BLUE);
        mWavePaint.setStyle(Paint.Style.FILL_AND_STROKE);

        mTextPaint = new Paint();
        mTextPaint.setTextAlign(Paint.Align.CENTER);
        mTextPaint.setTextSize(50);
        mTextPaint.setColor(Color.BLACK);

        mPaintCir = new Paint();
        mPaintCir.setAntiAlias(true);
        mPaintCir.setColor(Color.GREEN);
        mPaintCir.setStrokeWidth(20);
        mPaintCir.setStyle(Paint.Style.STROKE);

        mBgPaintCir = new Paint();
        mBgPaintCir.setAntiAlias(true);
        mBgPaintCir.setColor(Color.GRAY);
        mBgPaintCir.setStrokeWidth(20);
        mBgPaintCir.setStyle(Paint.Style.STROKE);

        mWavePath = new Path();
        mWaveStrokePath = new Path();

        // Init Wave.
        mShaderMatrix = new Matrix();
        mWavePaint = new Paint();
        // The ANTI_ALIAS_FLAG bit AntiAliasing smooths out the edges of what is being drawn,
        // but is has no impact on the interior of the shape.
        mWavePaint.setAntiAlias(true);
        mWaveBgPaint = new Paint();
        mWaveBgPaint.setAntiAlias(true);

        // Init Border
        mBorderPaint = new Paint();
        mBorderPaint.setAntiAlias(true);
        mBorderPaint.setStyle(Paint.Style.STROKE);
        mBorderPaint.setStrokeWidth(0);
        mBorderPaint.setColor(DEFAULT_WAVE_COLOR);
        // Init wave Animation
        initWaveAnimation();
        // Init AmplitudeRatio
        float amplitudeRatioAttr = DEFAULT_AMPLITUDE_VALUE / 1000;
        mAmplitudeRatio = (amplitudeRatioAttr > DEFAULT_AMPLITUDE_RATIO) ? DEFAULT_AMPLITUDE_RATIO : amplitudeRatioAttr;
        // Init Wave
        mWaveColor = DEFAULT_WAVE_COLOR;
        mWaveBgColor = DEFAULT_WAVE_BACKGROUND_COLOR;
        mWaveBgPaint.setColor(mWaveBgColor);

        setFocusable(true);
        setFocusableInTouchMode(true);
        this.setKeepScreenOn(true);

    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        setMeasuredDimension(MiscUtil.measure(widthMeasureSpec,mDefaultSize),MiscUtil.measure(heightMeasureSpec,mDefaultSize));
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        /*确定中心点*/
        mCenterPoint.x = w / 2;
        mCenterPoint.y = h / 2;
        /*确定高宽最小长度*/
        int minSize = (int) Math.min((w - getPaddingLeft() - getPaddingRight() - mArcWidth * 2), (h - getPaddingTop() - getPaddingBottom() - mArcWidth * 2));
        /*确定圆的半径*/
        mRadius = minSize / 2;
        /*确定内切圆边界*/
        mRectF.left = mCenterPoint.x - mRadius - mArcWidth / 2;
        mRectF.top = mCenterPoint.y - mRadius - mArcWidth / 2;
        mRectF.right = mCenterPoint.x + mRadius + mArcWidth / 2;
        mRectF.bottom = mCenterPoint.y + mRadius + mArcWidth / 2;
        updateWaveShader();
    }

    private void updateWaveShader() {
        // IllegalArgumentException: width and height must be > 0 while loading Bitmap from View
        // http://stackoverflow.com/questions/17605662/illegalargumentexception-width-and-height-must-be-0-while-loading-bitmap-from
        if (bitmapBuffer == null || haveBoundsChanged()) {
            if (bitmapBuffer != null)
                bitmapBuffer.recycle();
            int width = getMeasuredWidth();
            int height = getMeasuredHeight();
            if (width > 0 && height > 0) {
                double defaultAngularFrequency = 2.0f * Math.PI / DEFAULT_WAVE_LENGTH_RATIO / width;
                float defaultAmplitude = height * DEFAULT_AMPLITUDE_RATIO;
                mDefaultWaterLevel = height * DEFAULT_WATER_LEVEL_RATIO;
                float defaultWaveLength = width;

                Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
                Canvas canvas = new Canvas(bitmap);

                Paint wavePaint = new Paint();
                wavePaint.setStrokeWidth(2);
                wavePaint.setAntiAlias(true);

                // Draw default waves into the bitmap.
                // y=Asin(ωx+φ)+h
                final int endX = width + 1;
                final int endY = height + 1;

                float[] waveY = new float[endX];

                wavePaint.setColor(adjustAlpha(mWaveColor, 0.3f));
                for (int beginX = 0; beginX < endX; beginX++) {
                    double wx = beginX * defaultAngularFrequency;
                    float beginY = (float) (mDefaultWaterLevel + defaultAmplitude * Math.sin(wx));
                    canvas.drawLine(beginX, beginY, beginX, endY, wavePaint);
                    waveY[beginX] = beginY;
                }

                wavePaint.setColor(mWaveColor);
                final int wave2Shift = (int) (defaultWaveLength / 4);
                for (int beginX = 0; beginX < endX; beginX++) {
                    canvas.drawLine(beginX, waveY[(beginX + wave2Shift) % endX], beginX, endY, wavePaint);
                }

                // Use the bitamp to create the shader.
                mWaveShader = new BitmapShader(bitmap, Shader.TileMode.REPEAT, Shader.TileMode.CLAMP);
                this.mWavePaint.setShader(mWaveShader);
            }
        }
    }

    private boolean haveBoundsChanged() {
        return getMeasuredWidth() != bitmapBuffer.getWidth() ||
                getMeasuredHeight() != bitmapBuffer.getHeight();
    }
    /**
     * Transparent the given color by the factor
     * The more the factor closer to zero the more the color gets transparent
     *
     * @param color  The color to transparent
     * @param factor 1.0f to 0.0f
     * @return int - A transplanted color
     */
    private int adjustAlpha(int color, float factor) {
        int alpha = Math.round(Color.alpha(color) * factor);
        int red = Color.red(color);
        int green = Color.green(color);
        int blue = Color.blue(color);
        return Color.argb(alpha, red, green, blue);
    }
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawColor(Color.CYAN);
        drawLine(canvas);
        drawClicle(canvas);
        drawWave(canvas);
        drawProgress(canvas);
    }

    private void drawWave(Canvas canvas) {
        mCanvasSize = canvas.getWidth();
        if (canvas.getHeight() < mCanvasSize) {
            mCanvasSize = canvas.getHeight();
        }
        // Draw Wave.
        // Modify paint shader according to mShowWave state.
        if (mWaveShader != null) {
            // First call after mShowWave, assign it to our paint.
            if (mWavePaint.getShader() == null) {
                mWavePaint.setShader(mWaveShader);
            }

            // Sacle shader according to waveLengthRatio and amplitudeRatio.
            // This decides the size(waveLengthRatio for width, amplitudeRatio for height) of waves.
            mShaderMatrix.setScale(1, mAmplitudeRatio / DEFAULT_AMPLITUDE_RATIO, 0, mDefaultWaterLevel);
            // Translate shader according to waveShiftRatio and waterLevelRatio.
            // This decides the start position(waveShiftRatio for x, waterLevelRatio for y) of waves.
            mShaderMatrix.postTranslate(mWaveShiftRatio * getWidth(),
                    (DEFAULT_WATER_LEVEL_RATIO - mWaterLevelRatio) * getHeight());

            // Assign matrix to invalidate the shader.
            mWaveShader.setLocalMatrix(mShaderMatrix);

            // Get borderWidth.
            float borderWidth = mBorderPaint.getStrokeWidth();

            // The default type is triangle.
            if (borderWidth > 0) {
                canvas.drawCircle(getWidth() / 2f, getHeight() / 2f,
                        (getWidth() - borderWidth) / 2f - 1f, mBorderPaint);
            }

            float radius = getWidth() / 2f - borderWidth;
            // Draw background
            canvas.drawCircle(getWidth() / 2f, getHeight() / 2f, radius, mWaveBgPaint);
            canvas.drawCircle(getWidth() / 2f, getHeight() / 2f, radius, mWavePaint);
        } else {
            mWavePaint.setShader(null);
        }
    }

    private void drawProgress(Canvas canvas) {
        canvas.drawText(String.format("%.0f", mCurrVaule) + "%000", mCenterPoint.x, mCenterPoint.y + MiscUtil.measureTextHeight(mTextPaint) / 2, mTextPaint);
    }

    private void drawLine(Canvas canvas) {
        /*绘制基准新*/
        canvas.drawLine(0, mCenterPoint.y, mCenterPoint.x*2, mCenterPoint.y, linePaint);
        canvas.drawLine(mCenterPoint.x, 0, mCenterPoint.x, mCenterPoint.y * 2, linePaint);
    }

    private void drawClicle(Canvas canvas) {
        float currentAngle = 360 * mPrecent;
        Log.d(Tag, "mPrecent=" + mPrecent + "  currentAngle=" + currentAngle);

        /*绘制圆背景*/
        canvas.drawArc(mRectF, currentAngle, 360 - currentAngle, false, mBgPaintCir);
        /*绘制进度*/
        canvas.drawArc(mRectF, 0, currentAngle, false, mPaintCir);
    }

    public void setProgress(float value) {
        if(value < 0){
            throw new IllegalArgumentException("progress not less than 0");
        }
        if(value > mMaxValue){
            value = mMaxValue;
        }
        /*初始化圆环*/
        initCircle(value);
        /*初始波纹化*/
        initWave(value);
    }

    private void initCircle(float value) {
        float start = mPrecent;
        float end = value / mMaxValue;
        Log.d(Tag, "start=" + start + "  end=" + end);
        startAnimation(start,end);
    }

    private void initWave(float value) {
        ObjectAnimator waterLevelAnim = ObjectAnimator.ofFloat(this, "waterLevelRatio", mWaterLevelRatio, ((float) value / 100));
        waterLevelAnim.setDuration(1000);
        waterLevelAnim.setInterpolator(new DecelerateInterpolator());
        AnimatorSet animatorSetProgress = new AnimatorSet();
        animatorSetProgress.play(waterLevelAnim);
        animatorSetProgress.start();
    }

    private void startAnimation(float start, float end) {
        ValueAnimator anim = ValueAnimator.ofFloat(start, end);
        anim.setDuration(1800L);
        anim.setInterpolator(new AccelerateDecelerateInterpolator());
        anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                mPrecent = (float) animation.getAnimatedValue();
                mCurrVaule = mPrecent * mMaxValue;
                invalidate();
            }
        });
        anim.start();
    }

    private void initWaveAnimation() {

    }
}
